package top.huxy.boot.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import top.huxy.boot.config.Constant;
import top.huxy.boot.config.RedisKeyConfig;
import top.huxy.boot.exception.RequestAbortedException;
import top.huxy.boot.mapper.QqUserMapper;
import top.huxy.boot.mapper.UserDao;
import top.huxy.boot.model.dto.RegisterUserByEmailForm;
import top.huxy.boot.model.dto.UserLoginForm;
import top.huxy.boot.model.entity.QqUser;
import top.huxy.boot.model.entity.User;
import top.huxy.boot.model.vo.user.VO_User;
import top.huxy.boot.service.MailService;
import top.huxy.boot.service.UserService;
import top.huxy.boot.service.ImageService;
import top.huxy.boot.util.IPAddressUtils;
import top.huxy.boot.util.RegexUtils;
import top.huxy.boot.util.RequestUtils;
import top.huxy.boot.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.HashMap;

/**
 * @Classname UserServiceImpl
 * @Description UserServiceImpl
 * @Date 2021/9/5 9:04
 * @Created by huxy
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {


    @Autowired
    RestTemplate restTemplate;

    @Autowired
    QqUserMapper qqUserMapper;

    @Autowired
    UserDao userDao;

    @Autowired
    ImageService imageService;

    @Autowired
    MailService mailService;

    @Value("${huxy.app.qq-appid}")
    private String qqAppid;

    @Autowired
    public RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional
    public VO_User qqLogin(String openId, String accessToken) {
        //请求QQ登录接口
        HashMap<String, String> params = new HashMap<>();
        params.put("oauth_consumer_key", qqAppid);
        params.put("openid", openId);
        params.put("access_token", accessToken);
        String response = restTemplate.getForObject("https://graph.qq.com/user/get_user_info?oauth_consumer_key={oauth_consumer_key}&openid={openid}&access_token={access_token}", String.class, params);
        QQLoginResult qqLoginResult = JSON.parseObject(response, QQLoginResult.class);
        if (qqLoginResult == null) {
            log.info("qqLoginResult is null");
            return null;
        }
        Integer ret = qqLoginResult.getRet();
        if (ret == null || ret != 0) {
            log.info("QQ登录失败,{}", qqLoginResult);
            return null;
        }
        VO_User voqqUser = this.getVOQQUser(openId, qqLoginResult);
        String token = StringUtils.unique();
        voqqUser.setToken(token);
        //保存用户登录行为
        setVOUserByToken(token, voqqUser);

        HttpServletResponse response0 = RequestUtils.getCurrentResponse();
        ResponseCookie responseCookie = ResponseCookie.from(Constant.USER_TOKEN_NAME, token)
                .sameSite("None")
                .path("/")
                .maxAge(Duration.ofHours(24))
                .secure(true)
                .build();
        response0.setHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
        return voqqUser;
    }

    @Data
    public static class QQLoginResult {
        Integer ret;
        String msg;
        String nickname;
        String figureurl_qq_1;
        String gender;
    }

    @Transactional(rollbackFor = Exception.class)
    public VO_User getVOQQUser(String openId, QQLoginResult qqLoginResult) {
        /*
        qq_user是否存在
            存在:
                获取qq_user对象
                根据qq_user的user_id从user表中获取vo_user对象
            不存在:
                插入并获取一个空user对象a
                openId和a.user_id插入到qq_user中
                返回a的vo形式
         */
        QqUser qqUser = qqUserMapper.getQQUser(openId);
        if (qqUser != null) {
            return userDao.getVOUSerByUserId(qqUser.getUserId());
        } else {
            User emptyUser = new User();
            String id = StringUtils.unique();
            emptyUser.setCreateTime(System.currentTimeMillis());
            emptyUser.setGender(qqLoginResult.getGender());
            emptyUser.setId(id);
            emptyUser.setNickName(qqLoginResult.getNickname());
            emptyUser.setHead(qqLoginResult.getFigureurl_qq_1());
            userDao.insertUser(emptyUser);
            qqUserMapper.insertQQUser(openId, id);

            VO_User vo_user = new VO_User();
            vo_user.setId(id);
            vo_user.setGender(qqLoginResult.getGender());
            vo_user.setNickName(qqLoginResult.getNickname());
            return vo_user;
        }
    }

    @Override
    public void requestEmailYzm(String email, String yzm) {
        if (!RegexUtils.isEmail(email)) {
            throw new RequestAbortedException("非法邮箱格式");
        }

        User user = userDao.getUserByUserName(email);
        if (user == null) {
            if (imageService.checkYzm(yzm)) {
                throw new RequestAbortedException("验证码错误");
            }
            mailService.sendEmailYzmMail(email);
        } else {
            throw new RequestAbortedException("邮箱已被注册");
        }
    }

    @Override
    public boolean checkEmailYzm(String email, String yzm) {
        if (StringUtils.isEmpty(yzm)) {
            return false;
        }
        String redisKey = RedisKeyConfig.EMAIL_YZM_PREFIX + email;
        Object rightYzm = redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isEmpty(rightYzm)) {
            return false;
        }
        if (yzm.equalsIgnoreCase(rightYzm.toString())) {
            redisTemplate.delete(redisKey);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void registerUserByEmail(RegisterUserByEmailForm form) {
        form.validate();
        if (!this.checkEmailYzm(form.getEmail(), form.getYzm())) {
            throw new RequestAbortedException("验证码错误");
        }
        User user = new User();
        String id = StringUtils.unique();
        user.setId(id);
        user.setCreateTime(System.currentTimeMillis());
        //密码加密存储
        user.setPassword(StringUtils.encryption(form.getPassword()));
        user.setUsername(form.getEmail());
        int maxUserNameLength = 20;
        if (form.getEmail().length() > maxUserNameLength) {
            user.setNickName(form.getEmail().substring(0, maxUserNameLength - 1));
        } else {
            user.setNickName(form.getEmail());
        }

        try {
            userDao.insertUser(user);
        } catch (Exception e) {
            throw new RequestAbortedException("注册失败,写入数据库失败", e);
        }
    }

    @Override
    public VO_User userLogin(UserLoginForm form) {
        form.validate();
        if (imageService.checkYzm(form.getYzm())) {
            throw new RequestAbortedException("验证码错误");
        }
        VO_User user = userDao.getVOUSerByUserNameAndPassword(form.getUsername(), StringUtils.encryption(form.getPassword()));
        if (user == null) {
            log.info("登录失败,{}", form);
            throw new RequestAbortedException("用户名或密码错误");
        }
        String token = StringUtils.unique();
        user.setToken(token);
        setVOUserByToken(token, user);


        HttpServletResponse response = RequestUtils.getCurrentResponse();
        ResponseCookie responseCookie = ResponseCookie.from(Constant.USER_TOKEN_NAME, token)
                .sameSite("None")
                .path("/")
                .maxAge(Duration.ofHours(24))
                .secure(true)
                .build();
        response.setHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
        return user;
    }

    private void setVOUserByToken(String token, VO_User user) {
        String ip = IPAddressUtils.getRequestIp();
        redisTemplate.opsForHash().put(ip, token, user);
    }

    @Override
    public VO_User getVOUserByToken(String userToken) {
        String ip = IPAddressUtils.getRequestIp();
        Object o = redisTemplate.opsForHash().get(ip, userToken);
        if (o instanceof VO_User) {
            return ((VO_User) o);
        }
        return null;
    }

    @Override
    public void logOut(String userToken) {
        String ip = IPAddressUtils.getRequestIp();
        redisTemplate.opsForHash().delete(ip, userToken);
        HttpServletResponse response = RequestUtils.getCurrentResponse();
        ResponseCookie responseCookie = ResponseCookie.from(Constant.USER_TOKEN_NAME, "")
                .sameSite("None")
                .path("/")
                .maxAge(-1)
                .secure(true)
                .build();
        response.setHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
    }
}
